#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <signal.h>

#include <xmlrpc-c/server_abyss.h>
#include <xmlrpc-c/client_global.h>

#define SERVER_NUMBER 10
#define SERVER_PORT 8080
#define SERVER_URL_LENGTH 50

char all_server_urls[SERVER_NUMBER][SERVER_URL_LENGTH];

void start_server(int);
void stop_server(pid_t);
void start_client();
void start_client_async();

int main() {

    pid_t svr_pid[1024];
    int i;

    printf("\n**********************\n");

    /* start servers */
    for (i = 0; i < SERVER_NUMBER; i++) {
        
        svr_pid[i] = fork();

        if(svr_pid[i] == -1){
            fprintf(stderr, "server process create fail in main\n");
        }

        if ( svr_pid[i] == 0 ) {
            start_server(SERVER_PORT + i);
            return 0;
        }
    }

    sleep(2);

            
    for (i = 0; i < SERVER_NUMBER; i++) {
        sprintf(all_server_urls[i], "http://localhost:%d/RPC2", (SERVER_PORT + i));
    }

    /* run client */
    //start_client();
    start_client_async();

    for (i = 0; i < SERVER_NUMBER; i++) {
        stop_server(svr_pid[i]);
    }

    return 0;
}

/* server */

static xmlrpc_value *sample_add(xmlrpc_env *   const envP,
           xmlrpc_value * const paramArrayP,
           void *         const serverInfo,
           void *         const channelInfo) {

    xmlrpc_int32 x, y, z;

    /* Parse our argument array. */
    xmlrpc_decompose_value(envP, paramArrayP, "(ii)", &x, &y);
    if (envP->fault_occurred)
        return NULL;

    /* Add our two numbers. */
    z = x + y;

    /* Sometimes, make it look hard (so client can see what it's like
       to do an RPC that takes a while).
    */
    if (y == 1)
        sleep(3);

    /* Return our result. */
    return xmlrpc_build_value(envP, "i", z);
}

void start_server(int port) {

    struct xmlrpc_method_info3 const methodInfo = {
        /* .methodName     = */ "sample.add",
        /* .methodFunction = */ &sample_add,
    };
    xmlrpc_server_abyss_parms serverparm;
    xmlrpc_registry * registryP;
    xmlrpc_env env;

    xmlrpc_env_init(&env);

    registryP = xmlrpc_registry_new(&env);

    xmlrpc_registry_add_method3(&env, registryP, &methodInfo);

    /* In the modern form of the Abyss API, we supply parameters in memory
       like a normal API.  We select the modern form by setting
       config_file_name to NULL: 
    */
    serverparm.config_file_name = NULL;
    serverparm.registryP        = registryP;
    serverparm.port_number      = port;
    serverparm.log_file_name    = "/tmp/xmlrpc_log";

    printf("[Server %d] Start running on port %d...\n", getpid(), port);

    xmlrpc_server_abyss(&env, &serverparm, XMLRPC_APSIZE(log_file_name));
}

void stop_server(pid_t pid) {
    kill(pid, SIGTERM);
    printf("[Server %d] Stopped.\n", pid);
}


/* client */

#define NAME "Xmlrpc-c Client"
#define VERSION "1.0"

static void die_if_fault_occurred (xmlrpc_env * const envP) {
    if (envP->fault_occurred) {
        fprintf(stderr, "ERROR: %s (%d)\n",
                envP->fault_string, envP->fault_code);
        exit(1);
    }
}



void start_client() {

    xmlrpc_env env;
    xmlrpc_value * resultP;
    xmlrpc_int32 sum;

    char *servers_url[SERVER_NUMBER];
    const char * const methodName = "sample.add";

    int i;

    /* Initialize our error-handling environment. */
    xmlrpc_env_init(&env);

    /* Start up our XML-RPC client library. */
    xmlrpc_client_init2(&env, XMLRPC_CLIENT_NO_FLAGS, NAME, VERSION, NULL, 0);
    die_if_fault_occurred(&env);


    for (i = 0; i < SERVER_NUMBER; i++) {
        servers_url[i] = all_server_urls[i];
    }

    printf("[Client %d] Making XMLRPC call to all servers.\n", getpid());
    /* Make the remote procedure call */
    /*resultP = xmlrpc_client_call(&env, serverUrl, methodName,
                                 "(ii)", (xmlrpc_int32) 5, (xmlrpc_int32) 7);*/
    resultP = xmlrpc_client_call_multi(&env, servers_url, SERVER_NUMBER, methodName,
                                 "(ii)", (xmlrpc_int32) 5, (xmlrpc_int32) 7);
    die_if_fault_occurred(&env);

    /* Get our sum and print it out. */
    xmlrpc_read_int(&env, resultP, &sum);
    die_if_fault_occurred(&env);
    printf("[Client %d] XMLRPC call returned. The sum is %d\n", getpid(), sum);
    
    /* Dispose of our result value. */
    xmlrpc_DECREF(resultP);

    /* Clean up our error-handling environment. */
    xmlrpc_env_clean(&env);
    
    /* Shutdown our XML-RPC client library. */
    xmlrpc_client_cleanup();
}



static void 
handle_sample_add_response(const char *   const serverUrl,
                           const char *   const methodName,
                           xmlrpc_value * const paramArrayP,
                           void *         const user_data,
                           xmlrpc_env *   const faultP,
                           xmlrpc_value * const resultP) {

    xmlrpc_env env;
    xmlrpc_int addend, adder;
    
    /* Initialize our error environment variable */
    xmlrpc_env_init(&env);

    /* Our first four arguments provide helpful context.  Let's grab the
       addends from our parameter array. 
    */
    xmlrpc_decompose_value(&env, paramArrayP, "(ii)", &addend, &adder);
    die_if_fault_occurred(&env);

    if (faultP->fault_occurred)
        printf("The RPC failed.  %s\n", faultP->fault_string);
    else {
        xmlrpc_int sum;

        xmlrpc_read_int(&env, resultP, &sum);
        die_if_fault_occurred(&env);

        printf("[Client %d] Response handler is called. The sum is %d.\n", getpid(), sum);
    }
}

void start_client_async() {

    char *servers_url[SERVER_NUMBER];
    const char * const methodName = "sample.add";
    int i;

    xmlrpc_env env;
    xmlrpc_int adder;

    /* Initialize our error-handling environment. */
    xmlrpc_env_init(&env);

    /* Start up our XML-RPC client library. */
    xmlrpc_client_init2(&env, XMLRPC_CLIENT_NO_FLAGS, NAME, VERSION, NULL, 0);
    die_if_fault_occurred(&env);
    

    for (i = 0; i < SERVER_NUMBER; i++) {
        servers_url[i] = all_server_urls[i];
    }
    
    printf("[Client %d] Making asynchronous XMLRPC call to all servers.\n", getpid());
    /* request the remote procedure call */
    /*xmlrpc_client_call_asynch(servers_url[0], methodName,
                              handle_sample_add_response, NULL,
                              "(ii)", (xmlrpc_int32) 5, (xmlrpc_int32) 10);*/
    xmlrpc_client_call_asynch_multi(servers_url, SERVER_NUMBER, methodName,
                              handle_sample_add_response, NULL,
                              "(ii)", (xmlrpc_int32) 5, (xmlrpc_int32) 10);
    die_if_fault_occurred(&env);
    
    printf("[Client %d] RPCs all requested.\n", getpid());

    printf("[Client %d] Waiting for handling responses...\n", getpid());

    /* Wait for all RPCs to be done.  With some transports, this is also
       what causes them to go.
    */
    xmlrpc_client_event_loop_finish_asynch();

    printf("[Client %d] All RPCs finished.\n", getpid());

    /* Clean up our error-handling environment. */
    xmlrpc_env_clean(&env);
    
    /* Shutdown our XML-RPC client library. */
    xmlrpc_client_cleanup();
}

